use std::{
    collections::HashMap,
    sync::{Arc, Mutex, OnceLock},
};

pub trait InitConfig {
    fn init_config() -> Self;
}

#[derive(Debug)]
pub struct Conf<T: Send + Sync + 'static + InitConfig> {
    // thread safe when using Send + Sync
    value: OnceLock<Arc<Mutex<StructInfo<T>>>>,
}

#[derive(Debug, Clone)]
pub struct StructInfo<T> {
    info: T,
}

#[derive(Debug, Clone)]
pub struct ConfigKV<T: Clone> {
    pub value: OnceLock<Arc<Mutex<HashMap<String, T>>>>,
}

// methods for ConfigKV: new, get, put_config_by_key, get_config_by_key
impl<T: Clone> ConfigKV<T> {
    pub const fn new() -> Self {
        let once_lock: OnceLock<Arc<Mutex<HashMap<String, T>>>> = OnceLock::new();
        Self { value: once_lock }
    }

    fn get(&self) -> Arc<Mutex<HashMap<String, T>>> {
        let once_lock: &OnceLock<Arc<Mutex<HashMap<String, T>>>> = &self.value;
        once_lock
            .get_or_init(|| Arc::new(Mutex::new(HashMap::<String, T>::new())))
            .clone()
    }

    pub fn put_config_by_key(&self, config_key: String, t: T) {
        let once_lock: Option<&Arc<Mutex<HashMap<String, T>>>> = self.value.get();

        if let None = once_lock {
            self.get();
        }

        let kv_wrap: &Arc<Mutex<HashMap<String, T>>> = self.value.get().unwrap();

        let mut config: std::sync::MutexGuard<'_, HashMap<String, T>> = kv_wrap.lock().unwrap();

        config.insert(config_key, t);
    }

    pub fn get_config_by_key(&self, config_key: &String) -> Option<T> {
        let once_lock = self.value.get();

        if let None = once_lock {
            self.get();
        }

        let kv_wrap = self.value.get().unwrap();
        let config = kv_wrap.lock().unwrap();

        let result_t = config.get(config_key);

        match result_t {
            None => None,
            Some(res) => {
                let res = res.clone();
                Some(res)
            }
        }
    }
}

// methods for Conf: new, get, get_struct,
impl<T: InitConfig + Clone + Send + Sync + 'static> Conf<T> {
    pub const fn new() -> Self {
        let once_lock = OnceLock::new();
        Conf { value: once_lock }
    }

    fn get(&self) -> Arc<Mutex<StructInfo<T>>> {
        self.value
            .get_or_init(|| {
                Arc::new(Mutex::new(StructInfo {
                    info: T::init_config(),
                }))
            })
            .clone()
    }

    pub fn get_struct(&self) -> &'static T {
        let once_lock = self.value.get();

        if let None = once_lock {
            self.get();
        }

        let xx = self.value.get().unwrap();
        let x = xx.lock().unwrap();
        unsafe {
            let x = &x.info as *const T;
            &*x
        }
    }

    pub fn get_struct_into(&self) -> T {
        let once_lock = self.value.get();

        if let None = once_lock {
            self.get();
        }

        let st_info_wrap = self.value.get().unwrap();
        let st_info = st_info_wrap.lock().unwrap();

        st_info.info.clone()
    }

    pub fn set_struct(&mut self, t: T) {
        let xx = self.value.get();
        if let None = xx {
            self.get();
        }

        let mut x = self.value.get();
        match x {
            Some(_x) => match _x.lock() {
                Ok(_) => {}
                Err(_) => {}
            },
            None => {}
        }
    }
}

// TODO: mod test, I guess always put the test to the top of a file
